package cn.comtom.ewbs.signature.service.impl;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.bind.DatatypeConverter;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSON;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import cn.comtom.ewbs.signature.common.exception.SignException;
import cn.comtom.ewbs.signature.common.utils.ConfigFileUtil;
import cn.comtom.ewbs.signature.common.vo.request.CertApplyReq;
import cn.comtom.ewbs.signature.common.vo.response.CertResp;
import cn.comtom.ewbs.signature.service.AbstractSignService;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.tass.exceptions.YJException;
import cn.tass.yingjgb.YingJGBCALLDLL;
import lombok.extern.slf4j.Slf4j;

@Service("sjj1507")
@Slf4j
public class Sjj1507ServiceImpl extends AbstractSignService implements InitializingBean, DisposableBean{

    
    @Value("${sign.testServer.certApplyUrl}")
    private  String certApplyUrl;
    
    @Value("${sign.testServer.psCertNo}")
    private String platFormCert;
    
    // 连接设备的类型标识  0 ：代表SJJ1507密码机 , 1:代表SJJ1313密码器
 	public static final Integer flag = 0;

    @Override
    public String getByteSign(byte[] data) throws Exception {
    	log.info("getByteSign use:{}",signType);
        String xmlSign = YingJGBCALLDLL.platformCalculateSignature(dataType, data);
        xmlSign = DatatypeConverter.printHexBinary(DatatypeConverter.parseBase64Binary(xmlSign));
        return xmlSign;
    }
    
    @Override
    public String getFileSign(byte[] data) throws Exception {
    	log.info("getFileSign use:{}",signType);
        String xmlSign = YingJGBCALLDLL.platformCalculateSignature(dataType, data);
        return xmlSign;
    }
    
    public  Boolean verifyByteSign(byte[] data, String signValue) throws YJException {
    	log.info("verifyByteSign use:{}",signType);
		// 验证数据签名
		signValue = DatatypeConverter.printBase64Binary(DatatypeConverter.parseHexBinary(signValue));
		boolean result = YingJGBCALLDLL.platformVerifySignature(dataType, data, signValue);
		return result;
    }
    
    @Override
    public  Boolean verifyFileSign(byte[] data, String signValue) throws YJException {
    	log.info("verifyFileSign use:{}",signType);
    	// 验证数据签名
		boolean result = YingJGBCALLDLL.platformVerifySignature(dataType, data, signValue);
		return result;
    }
    
    @Override
    public  boolean openDevice() {
        try {
            synchronized (YingJGBCALLDLL.class) {
            	YingJGBCALLDLL.openDevice(flag);
            	return true;
            }
        } catch (Exception e) {
            log.error("use sjj1507 open device failed {}", e);
            throw new SignException(String.format("打开sjj1507设备失败-%s", e.getMessage()));
        }
    }

    @Override
    public  void closeDevice() {
        try {
        	log.info("关闭密码器");
            // 关闭sjj1507密码机
            YingJGBCALLDLL.closeDevice();
        } catch (Exception e) {
        	log.error("use sjj1507 close device failed.", e);
        }
    }

	@Override
	public CertResp certApply(CertApplyReq certApplyReq) {
		log.info("certApply use:{}",signType);
		CertResp certResp = new CertResp();
		String listStr = null;
		List<String> paraseCert = null;
		try {
			certApplyReq.getSourceCertNos().add(platFormCert);
			StringBuffer reqUrl = new StringBuffer(certApplyUrl);
			reqUrl.append("?path=");
			for (String sourceCertNo : certApplyReq.getSourceCertNos()) {
				reqUrl.append(sourceCertNo).append(".");
			}
			reqUrl.deleteCharAt(reqUrl.length() - 1);
			reqUrl.append("&opt=").append("reg");
			reqUrl.append("&SMSN=").append(certApplyReq.getDestCertNo());
			
			RestTemplate restTemplate = new RestTemplate();
			String message = restTemplate.getForObject(reqUrl.toString(), String.class);
			paraseCert = paraseCert(message);
			log.info("向安全代理获取信任列表结束，params:{},result:{}",JSON.toJSONString(certApplyReq),message);
			if(StringUtils.isEmpty(message)) {
				return certResp;
			}
			listStr = paraseToMap(message).get("LIST");
			if(StringUtils.isBlank(listStr)) {
				return certResp;
			}
		} catch (RestClientException e) {
			log.error("向安全代理获取信任列表异常，params:{},result:{}",JSON.toJSONString(certApplyReq));
			e.printStackTrace();
			throw new SignException("获取信任列表失败,正在重试,重试3次任然失败则重启签名服务！");
		}
		
		String data = DatatypeConverter.printHexBinary(DatatypeConverter.parseBase64Binary(listStr));
		certResp.setCertauth(data);
		certResp.setCerth(paraseCert);
		return certResp;
	}
	
	private List<String> paraseCert(String message) {
		int indexOf = message.indexOf(",<");
		String certStr = message.substring(indexOf+1);
		List<String> tagContent = getTagContent(certStr,"CertCtx");
		return tagContent;
	}
	  /**
     * @param source 要匹配的源文本 
     * @param element 标签名称 
     * @return  内容集合
     */
    private static List<String> getTagContent(String source, String element) {
        List<String> result = new ArrayList<String>();
        String reg = "<" + element + ">" + "(.+?)</" + element + ">";
        Matcher m = Pattern.compile(reg).matcher(source);
        while (m.find()) {
            String r = m.group(1);
            result.add(r);
        }
        return result;
    }
    
	private  Map<String,String> paraseToMap(String message){
		Map<String, String> resultMap = Maps.newHashMap();
		String[] split = message.split(",");
		if(split==null||split.length<2) {
			return resultMap;
		}
		for(int x=0;x<split.length;x++) {
			if(split[x].split("=").length>1)
			  resultMap.put(split[x].split("=")[0], split[x].split("=")[1]);
		}
		return resultMap;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		//openDevice();
	}

	@Override
	public void destroy() throws Exception {
		closeDevice();
	}
	
	@Override
	public Boolean importCert(List<String> certs) {
		log.info("importCert use:{}",signType);
		boolean result = true;
		try {
			for (String cert : certs) {
				byte[] certData = aschex_to_bcdhex(cert.getBytes());
				result = YingJGBCALLDLL.platformImportTrustedCert(certData);
				if(!result) {
					log.error("importCert failed! certStr:{}",cert);
					result = false;	
					continue;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	
	@Override
	public Boolean importCertauthList(String certauthList) {
		log.info("importCertauthList use:{}",signType);
		boolean result = true;
		try {
			byte[] certAuth = aschex_to_bcdhex(certauthList.getBytes());
			result = YingJGBCALLDLL.platformImportTrustedCertSnChain(certAuth);
			if (!result) {
				log.error("importCertauthList failed! CertAuth:{}",certauthList);
				result = false;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	public String trustedCertSnchainApply(List<String> lines) {
		if(CollectionUtils.isEmpty(lines)) {
			log.error("trustedCertSnchainApply文件内容为空！");
			return null;
		}
		RestTemplate restTemplate = new RestTemplate();
		StringBuffer reqSb = new StringBuffer();
		List<String> trustList = Lists.newArrayList();
		log.info(JSON.toJSONString(lines));
		for (String path : lines) {
			if(StringUtils.isBlank(path)) {
				continue;
			}
			//String smsn = StrUtil.subAfter(path, "SMSN=", true);
			reqSb.setLength(0);
			reqSb.append(certApplyUrl).append("?").append(path);
			String message = null;
			try {
				message = restTemplate.getForObject(reqSb.toString(), String.class);
				log.info("trustedCertSnchainApply.req:{},res:{}",reqSb.toString(),message);
			} catch (RestClientException e) {
				log.error("trustedCertSnchainApply申请信任证书和证书链失败，requestparam:{},errorinfo:{},exception:{}", path, e.getMessage(), e);
				continue;
			}
			if(StringUtils.isBlank(message) || !StrUtil.containsIgnoreCase(message, "RET_OK")) {
				log.error("trustedCertSnchainApply申请信任证书和证书链失败，requestparam:{},result：{}", path, message);
			}else {
				message = StrUtil.removeAll(StrUtil.removeAll(message, "\r\n"), "\n");
				message = message.replaceAll("RET_OK ", path.concat("=>"));
				//message = message.replaceFirst("000000000001", smsn);
				trustList.add(message);
			}
		}
		String respTxt = Joiner.on(System.lineSeparator()).join(trustList);
		File trustListFile = FileUtil.newFile("/trustedCertSnchainResp.txt");
		FileUtil.writeLines(trustList, trustListFile, "UTF-8");
		//FileUtil.writeString(respTxt, trustListFile, "UTF-8");
		return respTxt;
	}
	
	/**
	 * 十六进制字符串转为字节数组
	 * @param aschexByte
	 * @return
	 */
	private byte[] aschex_to_bcdhex(byte[] aschexByte) {
		int j = 0;
		if (aschexByte.length % 2 == 0) {
			j = aschexByte.length / 2;
			byte[] resTmp = new byte[j];
			for (int i = 0; i < j; i++) {
				resTmp[i] = ascToHex(aschexByte[2 * i], aschexByte[2 * i + 1]);
			}
			return resTmp;

		} else {
			j = aschexByte.length / 2 + 1;
			byte[] resTmp = new byte[j];
			for (int i = 0; i < j - 1; i++) {
				resTmp[i] = ascToHex((byte) aschexByte[2 * i],
						(byte) aschexByte[2 * i + 1]);
			}
			resTmp[j - 1] = ascToHex((byte) aschexByte[2 * (j - 1)], (byte) 0);
			return resTmp;
		}
	}

	private byte ascToHex(byte ch1, byte ch2) {
		byte ch;
		if (ch1 >= 'A')
			ch = (byte) ((ch1 - 0x37) << 4);
		else
			ch = (byte) ((ch1 - '0') << 4);
		if (ch2 >= 'A')
			ch |= (byte) (ch2 - 0x37);
		else
			ch |= (byte) (ch2 - '0');
		return ch;
	}
	
	@Override
	public boolean updateConfig(String configFilePath, String ip, Integer port, String signNo, String logPath, Integer conLimit, Integer timeOut) {
		Map<String,Map<String,String>> configFileMap = Maps.newHashMap();
		Map<String, String> configItemMap1 = Maps.newHashMap();
		configItemMap1.put("logPath", logPath);
		configFileMap.put("LOGGER", configItemMap1);
		
		Map<String, String> configItemMap2 = Maps.newHashMap();
		configItemMap2.put("host", ip);
		configItemMap2.put("port", port.toString());
		configItemMap2.put("timeout", timeOut.toString());
		configItemMap2.put("linkNum", conLimit.toString());
		configFileMap.put("HOST 1", configItemMap2);
		
		return ConfigFileUtil.updateIniFile(configFilePath, configFileMap);
	}
}
